package com.example.mq.rabbit.controller;

import com.example.mq.rabbit.model.OrderInfo;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/rabbit")
public class RabbitSendController implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
    private static final String EXCHANGE_NAME = "my_exchange";
    private static final String ROUTING_KEY = "my_routing";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 正常发送并被broker接收
     *
     * @return
     */
    @RequestMapping("send")
    public String send() {
        for (int i = 0; i < 10; i++) {
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setAddress("成都市高新区");
            orderInfo.setOrderId(String.valueOf(i));
            orderInfo.setProductName("华为P60:" + i);

            //设置回调关联的一个id
            String messageId = UUID.randomUUID().toString();
            log.info("开始发送消息，当前消息关联id为：{}", messageId);
            CorrelationData correlationData = new CorrelationData(messageId);

            MessageProperties messageProperties = new MessageProperties();
            messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            Message message = MessageBuilder.withBody(new Gson().toJson(orderInfo).getBytes(StandardCharsets.UTF_8))
                    .andProperties(messageProperties).build();
            //设置ack回调
            rabbitTemplate.setConfirmCallback(this);
            //退回消息的回调
            rabbitTemplate.setReturnCallback(this);
            rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING_KEY, message, correlationData);
        }
        return "ok";
    }

    /**
     * 设置一个非法的路由键，模拟消息被broker退回的情况，前提是
     * spring.rabbitmq.template.mandatory=true 当mandatory设置为true时，交换器无法根据自身的类型和路由键找到一个符合条件的队列，那么RabbitMQ会调用Basic.Return命令将消息返回给生产者。当为false时，则直接丢弃消息
     * <p>
     * spring.rabbitmq.publisher-returns=true 生产者回调确认机制，由回调来确定消息是否发布成功
     *
     * @return
     */
    @RequestMapping("send-return")
    public String sendAndReturn() {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setAddress("成都市高新区");
        orderInfo.setOrderId("111");
        orderInfo.setProductName("小米13");

        //设置回调关联的一个id
        String messageId = UUID.randomUUID().toString();
        log.info("开始发送消息，当前消息关联id为：{}", messageId);
        CorrelationData correlationData = new CorrelationData(messageId);

        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        Message message = MessageBuilder.withBody(new Gson().toJson(orderInfo).getBytes(StandardCharsets.UTF_8))
                .andProperties(messageProperties).build();
        //设置ack回调
        rabbitTemplate.setConfirmCallback(this);
        //退回消息的回调
        rabbitTemplate.setReturnCallback(this);
        //下面这个RoutingKey是没有绑定的，所以发不出去
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, "error.routing", message, correlationData);
        return "ok";
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (correlationData == null) {
            return;
        }
        Message returnedMessage = correlationData.getReturnedMessage();
        if (returnedMessage != null) {
            log.info("【confirm回调方法】,这是消息被退回的confirm,messageId={}", correlationData.getId());
            return;
        }
        String messageId = correlationData.getId();
        if (ack) {
            log.info("【confirm回调方法】,消息发布成功,messageId={}", messageId);
        } else {
            log.info("【confirm回调方法】,消息发布失败，messageId={}", messageId);
        }
    }

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("【returnedMessage回调方法】,消息被退回,message={},replyCode:{},replyText:{},exchange:{},routingKey:{}",
                new String(message.getBody()), replyCode, replyText, exchange, routingKey);

    }
}
